#pragma once

#include "Logger.hpp"

#include <sys/epoll.h>

using namespace std;
using namespace LogMudule;

// 前置声明
class Reactor;

// 对⼀个任意fd需要进行的IO事件的管理，以及事件触发之后通过回调函数处理事件
class Channel
{
    typedef function<void()> callback_t;

    static const int DefaultFd = -1;

private:
    int _fd;
    // fd关心的所有事件
    uint32_t _events;
    // fd关心的所有事件中已经就绪的事件
    uint32_t _revents;

    // 指向该Channel所属的Reactor
    Reactor *_belongs_reactor;

    // 读事件触发时的要调用的回调
    callback_t _read_callback;
    // 写事件触发时的要调用的回调(把要发送的数据添加到Buffer缓冲区()
    callback_t _write_callback;
    // 异常事件触发时的要调用的回调
    callback_t _error_callback;
    // 连接关闭时的要调用的回调
    callback_t _close_callback;
    // 任意事件触发之后，要调用的回调
    callback_t _event_callback;

private:
    // 把Channel自己对事件的更新同步到Reactor模块中的Epoller
    void Update();

public:
    Channel(Reactor *belongs_reactor, int fd = DefaultFd)
        : _fd(fd),
          _events(0),
          _revents(0),
          _belongs_reactor(belongs_reactor),
          _read_callback(nullptr),
          _write_callback(nullptr),
          _error_callback(nullptr),
          _close_callback(nullptr),
          _event_callback(nullptr)
    {
    }
    int Fd() const
    {
        return _fd;
    }
    uint32_t Events() const
    {
        return _events;
    }
    //事件就绪之后，通过SetRevents把就绪的事件设置给Channel
    void SetRevents(uint32_t events)
    {
        _revents = events;
    }
    // fd是否启动了读事件监控
    bool ReadAble()
    {
        // 如果按位与的结果不为0，就是关心
        if (_events & EPOLLIN)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    // fd是否启动了写事件监控
    bool WriteAble()
    {
        // 如果按位与的结果不为0，就是关心
        if (_events & EPOLLOUT)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    // 取消所有事件的关心
    void DisableAll()
    {
        _events = 0;
        // 同步到所属的Reactor的Epoll模块
        Update();
    }
    // 取消fd读事件关心
    void DisableRead()
    {
        _events &= (~EPOLLIN);
        // 同步到所属的Reactor的Epoll模块
        Update();
    }
    // 取消fd写事件关心
    void DisableWrite()
    {
        _events &= (~EPOLLOUT);
        // 同步到所属的Reactor的Epoll模块
        Update();
    }

    // 开启fd读事件关心
    void EnableRead()
    {
        _events |= (EPOLLIN | EPOLLRDHUP | EPOLLET);
        // 同步到所属的Reactor的Epoll模块
        Update();
    }

    // 开启fd写事件关心
    void EnableWrite()
    {
        _events |= (EPOLLOUT | EPOLLET);
        // 同步到所属的Reactor的Epoll模块
        Update();
    }

    // 把当前fd从epoll模型中移除
    void Remove();

    //判断当前fd是否在epoll模型中被监控
    bool IsSurveillance();


    // 设置读事件回调
    void SetReadCallback(const callback_t &cb)
    {
        _read_callback = cb;
    }
    // 设置写事件回调
    void SetWriteCallback(const callback_t &cb)
    {
        _write_callback = cb;
    }
    // 设置异常事件回调
    void SetErrorCallback(const callback_t &cb)
    {
        _error_callback = cb;
    }
    // 设置连接关闭事件回调
    void SetCloseCallback(const callback_t &cb)
    {
        _close_callback = cb;
    }
    // 设置任意事件触发都会调用的回调
    void SetEventCallback(const callback_t &cb)
    {
        _event_callback = cb;
    }

    // 有事件触发时，根据触发的事件类型，调用对应的回调函数
    void HandleEvents()
    {
        // 如果读事件就绪
        // 如果连接挂断事件就绪   ->其实就是最后触发一次读事件，作为保底,因为如果这个连接对应的TCP内核缓冲区中还有数据，不读的话，就再也看不到了
        // 如果有高优先级事件就绪
        if ((_revents & EPOLLIN) || (_revents & EPOLLRDHUP) || (_revents & EPOLLPRI))
        {
            // 就调用读事件回调
            if (_read_callback)
                _read_callback();
        }
        // 如果写事件就绪
        if ((_revents & EPOLLOUT))
        {
            // 就调用写事件回调
            if (_write_callback)
                _write_callback();
        }
        // 如果异常事件就绪
        if (_revents & EPOLLERR)
        {
            // 就调用异常事件回调
            if (_error_callback)
                _error_callback();
        }
        // 如果连接断开事件就绪
        if (_revents & EPOLLHUP)
        {
            // 就调用连接关闭事件回调
            if (_close_callback)
                _close_callback();
        }

        // 不管触发了什么事件，都要调用的回调
        if (_event_callback)
            _event_callback();
    }

    ~Channel()
    {
    }
};